﻿using Microsoft.Practices.Prism.Commands;
using System;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;

namespace HobSoft.SoccerBet2014.Client.ViewModels
{

    public abstract class CommandViewModel : MessageViewModel
    {
        [field: NonSerialized]
        private ConcurrentDictionary<ICommand, CommandState> commandStateContainer;
        [field: NonSerialized]
        protected ReadOnlyDictionary<ICommand, CommandState> CommandStateContainer { get; private set; }
        [field: NonSerialized]
        public event EventHandler<CommandState> OnCommandStart;
        [field: NonSerialized]
        public event EventHandler<CommandState> OnCommandOver;
        [field: NonSerialized]
        public event EventHandler<Exception> OnCommandError;

        public override void OnInitializeMessageOver()
        {
            commandStateContainer = new ConcurrentDictionary<ICommand, CommandState>();
            CommandStateContainer = new ReadOnlyDictionary<ICommand, CommandState>(commandStateContainer);
        }

        protected void RegisterCommand(ICommand command, string name)
        {
            if (commandStateContainer.ContainsKey(command)) return;
            CommandState state = new CommandState(command, name);
            commandStateContainer.TryAdd(command, state);
        }

        protected void ResetCommandState()
        {
            foreach (var cmdState in commandStateContainer.Values)
            {
                cmdState.State = CommandExecuteState.Freeing;
            }
        }

        protected bool CommandOnExecuting(ICommand command)
        {
            if (!commandStateContainer.ContainsKey(command)) return false;
            return commandStateContainer[command].State == CommandExecuteState.Executing;
        }

        protected void RaiseCommandStart(ICommand command, string message = "")
        {
            if (!commandStateContainer.ContainsKey(command))
                return;
            _lastExecutingCommand = commandStateContainer[command];
            commandStateContainer[command].State = CommandExecuteState.Executing;
            commandStateContainer[command].Message = message;
            State = ModelState.Executing;
            RaisePropertyChanged("HasCommandExecuting");
            RaisePropertyChanged("ExecutingCommandMessage");
            RaisePropertyChanged("ExecutingCommand");
            if (OnCommandStart != null)
                OnCommandStart(this, commandStateContainer[command]);

        }

        protected void RaiseCommandOver(ICommand command, string message = "")
        {
            if (!commandStateContainer.ContainsKey(command))
                return;
            _lastExecutingCommand = null;
            commandStateContainer[command].State = CommandExecuteState.Freeing;
            commandStateContainer[command].Message = message;
            State = ModelState.Freeing;
            RaisePropertyChanged("HasCommandExecuting");
            RaisePropertyChanged("ExecutingCommandMessage");
            RaisePropertyChanged("ExecutingCommand");
            if (OnCommandOver != null)
                OnCommandOver(this, commandStateContainer[command]);

        }

        protected virtual void RaiseCommandCanExecute(ICommand command)
        {
            var delegCommand = command as DelegateCommand;
            if (delegCommand == null) return;
            delegCommand.RaiseCanExecuteChanged();
        }

        protected virtual void RaiseCommandCanExecute<T>(ICommand command)
        {
            var delegCommand = command as DelegateCommand<T>;
            if (delegCommand == null) return;
            delegCommand.RaiseCanExecuteChanged();
        }

        protected virtual void RaiseCommandError(Exception error)
        {
            if (OnCommandError != null)
                OnCommandError(this, error);
        }

        protected virtual void RaiseDispatcherCommandError(Exception error)
        {
            if (OnCommandError != null)
            {
                Application.Current.Dispatcher.Invoke(new Action(delegate()
                {
                    OnCommandError(this, error);
                }));
            }
        }

        protected virtual bool HasAggregationExecuteing()
        {
            return false;
        }

        protected virtual CommandState GetAggregationExecutingCommand()
        {
            return null;
        }

        public bool HasCommandExecuting
        {
            get
            {
                return commandStateContainer.Values.Any(c => c.State == CommandExecuteState.Executing)
                    || HasAggregationExecuteing();
            }
        }

        private CommandState _lastExecutingCommand;
        public string ExecutingCommandMessage
        {
            get
            {
                var aggregCommand = GetAggregationExecutingCommand();
                return _lastExecutingCommand != null ? _lastExecutingCommand.Message :
                    aggregCommand != null ? aggregCommand.Message : "";
            }
        }

        public CommandState ExecutingCommand
        {
            get
            {
                return _lastExecutingCommand;
            }
        }

    }

    public enum CommandExecuteState
    {
        Executing,
        Freeing
    }

    public class CommandState
    {
        public CommandState(ICommand command, string name)
        {
            Command = command;
            Name = name;
            State = CommandExecuteState.Freeing;
        }
        public string Name { get; private set; }
        public ICommand Command { get; private set; }
        public CommandExecuteState State { get; set; }
        public string Message { get; set; }
    }
}
